import threading
import time
import numpy as np
import sounddevice as sd
import queue
from collections import defaultdict, deque

from audio_processing.base_processor import AudioProcessor


class AudioProcessingEngine:
    """音频处理引擎 - 使用 sounddevice"""

    def __init__(self, device_manager):
        self.device_manager = device_manager
        self.is_running = False
        self.processing_thread = None
        self.audio_stream = None
        self.audio_processors = {}  # node_id -> processor
        self.processing_graph = {}  # 处理图结构
        self.parameter_queue = queue.Queue()

        # 新增：处理状态管理
        self.processing_order = []  # 拓扑排序的处理顺序
        self.node_dependencies = defaultdict(list)  # 节点依赖关系
        self.node_outputs = defaultdict(dict)  # 节点输出缓存

        # 音频设置
        self.sample_rate = 44100
        self.buffer_size = 512
        self.channels = 2

    def build_processing_graph(self, canvas_data):
        """根据画布数据构建处理图 - 修复路由问题"""
        self.processing_graph = {}
        self.node_dependencies = defaultdict(list)

        # 构建节点映射
        node_map = {}
        for node_data in canvas_data.get('nodes', []):
            node_id = node_data['nodeId']
            node_map[node_id] = node_data

        # 构建连接关系
        for connection_data in canvas_data.get('connections', []):
            start_node = connection_data['startNodeId']
            end_node = connection_data['endNodeId']

            # 记录依赖关系
            self.node_dependencies[end_node].append(start_node)

            # 构建处理图连接
            if start_node not in self.processing_graph:
                self.processing_graph[start_node] = []
            self.processing_graph[start_node].append(end_node)

        # 计算拓扑排序的处理顺序
        self._calculate_processing_order(node_map)

        # 验证处理图完整性
        self._validate_processing_graph(node_map)

        print(f"构建处理图: {len(self.processing_order)} 个节点, {len(canvas_data.get('connections', []))} 个连接")
        print(f"处理顺序: {self.processing_order}")
        print(f"节点依赖: {dict(self.node_dependencies)}")

    def _validate_processing_graph(self, node_map):
        """验证处理图的完整性"""
        errors = []
        warnings = []

        # 检查所有节点是否都有对应的处理器
        for node_id, node_data in node_map.items():
            if node_id not in self.audio_processors:
                errors.append(f"节点 {node_id} ({node_data.get('nodeType', 'unknown')}) 缺少音频处理器")

        # 检查波形查看器的输入连接
        for node_id, node_data in node_map.items():
            if node_data.get('nodeType') == 'waveform_viewer':
                if node_id not in self.node_dependencies or not self.node_dependencies[node_id]:
                    warnings.append(f"波形查看器 {node_id} 没有输入连接，将无法显示波形数据")

        # 检查输出节点的输入连接
        for node_id, node_data in node_map.items():
            if node_data.get('nodeType') == 'output':
                if node_id not in self.node_dependencies or not self.node_dependencies[node_id]:
                    errors.append(f"输出节点 {node_id} 没有输入连接，音频将无法输出")

        # 检查输入节点的输出连接
        for node_id, node_data in node_map.items():
            if node_data.get('nodeType') == 'input':
                if node_id not in self.processing_graph or not self.processing_graph[node_id]:
                    warnings.append(f"输入节点 {node_id} 没有输出连接，音频数据将被丢弃")

        # 报告错误和警告
        if errors:
            error_msg = "处理图验证错误:\n" + "\n".join(f"• {error}" for error in errors)
            print(f"❌ {error_msg}")
            # 这里可以添加弹窗显示错误
            self._show_error_dialog("处理图配置错误", error_msg)

        if warnings:
            warning_msg = "处理图验证警告:\n" + "\n".join(f"• {warning}" for warning in warnings)
            print(f"⚠️ {warning_msg}")
            # 这里可以添加弹窗显示警告
            self._show_warning_dialog("处理图配置警告", warning_msg)

    def _show_error_dialog(self, title, message):
        """显示错误对话框"""
        try:
            from PySide6.QtWidgets import QMessageBox
            from PySide6.QtCore import QTimer

            # 使用定时器确保在主线程中显示对话框
            def show_dialog():
                msg = QMessageBox()
                msg.setIcon(QMessageBox.Critical)
                msg.setWindowTitle(title)
                msg.setText(message)
                msg.setStandardButtons(QMessageBox.Ok)
                msg.exec_()

            QTimer.singleShot(0, show_dialog)
        except Exception as e:
            print(f"无法显示错误对话框: {e}")

    def _show_warning_dialog(self, title, message):
        """显示警告对话框"""
        try:
            from PySide6.QtWidgets import QMessageBox
            from PySide6.QtCore import QTimer

            def show_dialog():
                msg = QMessageBox()
                msg.setIcon(QMessageBox.Warning)
                msg.setWindowTitle(title)
                msg.setText(message)
                msg.setStandardButtons(QMessageBox.Ok)
                msg.exec_()

            QTimer.singleShot(0, show_dialog)
        except Exception as e:
            print(f"无法显示警告对话框: {e}")

    def _calculate_processing_order(self, node_map):
        """计算拓扑排序的处理顺序"""
        in_degree = defaultdict(int)

        # 计算入度
        for node_id in node_map:
            in_degree[node_id] = 0

        for start_node, end_nodes in self.processing_graph.items():
            for end_node in end_nodes:
                in_degree[end_node] += 1

        # 拓扑排序
        queue = deque()
        for node_id in node_map:
            if in_degree[node_id] == 0:
                queue.append(node_id)

        self.processing_order = []
        while queue:
            node_id = queue.popleft()
            self.processing_order.append(node_id)

            if node_id in self.processing_graph:
                for neighbor in self.processing_graph[node_id]:
                    in_degree[neighbor] -= 1
                    if in_degree[neighbor] == 0:
                        queue.append(neighbor)

        # 检查是否有环
        if len(self.processing_order) != len(node_map):
            print("警告: 处理图中存在环，使用简单顺序")
            self.processing_order = list(node_map.keys())

    def start_processing(self):
        """启动音频处理"""
        if self.is_running:
            print("音频处理引擎已经在运行")
            return

        # 在处理前检查处理图
        if not self._validate_processing_setup():
            return

        self.is_running = True
        self.processing_thread = threading.Thread(
            target=self._processing_loop,
            daemon=True
        )
        self.processing_thread.start()
        print("音频处理引擎已启动")

    def _validate_processing_setup(self):
        """验证处理设置"""
        if not self.audio_processors:
            print("错误: 没有可用的音频处理器")
            return False

        if not self.processing_order:
            print("错误: 没有定义处理顺序")
            print(f"音频处理器: {list(self.audio_processors.keys())}")
            print(f"处理图: {self.processing_graph}")
            return False

        # 检查所有节点都有对应的处理器
        missing_processors = []
        for node_id in self.processing_order:
            if node_id not in self.audio_processors:
                missing_processors.append(node_id)

        if missing_processors:
            print(f"错误: 以下节点缺少处理器: {missing_processors}")
            return False

        print("处理设置验证通过")
        return True

    def _audio_callback(self, indata, outdata, frames, time, status):
        """音频流回调函数 - 修复路由问题"""
        if status:
            print(f"音频流状态: {status}")

        # 处理参数更新
        self._process_parameter_updates()

        try:
            # 清空输出缓存
            self.node_outputs.clear()

            # 如果没有活动的处理器，直接传递数据
            if not self.audio_processors:
                outdata[:] = indata
                return

            # 按照拓扑顺序处理每个节点
            final_output = None

            for node_id in self.processing_order:
                processor = self.audio_processors.get(node_id)
                if not processor or not processor.is_active:
                    continue

                # 获取输入数据
                input_data = None

                # 检查是否有依赖节点
                if node_id in self.node_dependencies and self.node_dependencies[node_id]:
                    # 有依赖节点，混合所有输入
                    mixed_input = None
                    for dep_node_id in self.node_dependencies[node_id]:
                        dep_output = self.node_outputs.get(dep_node_id, {}).get('output')
                        if dep_output is not None:
                            if mixed_input is None:
                                mixed_input = dep_output.copy()
                            else:
                                # 混合多个输入（简单相加）
                                mixed_input += dep_output

                    input_data = mixed_input
                else:
                    # 对于没有依赖的节点（如输入节点），使用音频输入
                    input_data = indata.copy()

                # 处理音频数据
                if input_data is not None:
                    node_output = processor.process(input_data)

                    # 缓存输出（波形查看器等节点可能没有输出，需要检查）
                    if node_output is not None:
                        self.node_outputs[node_id] = {'output': node_output}

                        # 如果是输出节点，使用其输出作为最终输出
                        if hasattr(processor, '__class__') and processor.__class__.__name__ == 'OutputProcessor':
                            final_output = node_output
                    else:
                        # 对于没有输出的节点（如波形查看器），创建一个空输出占位符
                        self.node_outputs[node_id] = {'output': None}
                else:
                    # 没有输入数据，设置空输出
                    self.node_outputs[node_id] = {'output': None}

            # 确定最终输出
            if final_output is None:
                # 如果没有找到输出节点，使用最后一个有输出的节点
                for node_id in reversed(self.processing_order):
                    node_output = self.node_outputs.get(node_id, {}).get('output')
                    if node_output is not None:
                        final_output = node_output
                        break

            # 如果仍然没有输出，使用原始输入
            if final_output is None:
                final_output = indata.copy()

            # 确保输出数据形状正确
            if final_output.shape != outdata.shape:
                final_output = self._adjust_audio_shape(final_output, outdata.shape)

            # 输出处理后的音频
            outdata[:] = final_output

        except Exception as e:
            print(f"音频处理回调错误: {e}")
            import traceback
            traceback.print_exc()
            # 如果处理出错，直接输出原始数据
            outdata[:] = indata

    def _adjust_audio_shape(self, audio_data, target_shape):
        """调整音频数据形状"""
        if audio_data.ndim == 1 and len(target_shape) == 2:
            # 一维转二维
            return audio_data.reshape(-1, 1)
        elif audio_data.ndim == 2 and len(target_shape) == 2:
            if audio_data.shape[1] != target_shape[1]:
                if audio_data.shape[1] == 1 and target_shape[1] == 2:
                    # 单声道转立体声
                    return np.repeat(audio_data, 2, axis=1)
                elif audio_data.shape[1] == 2 and target_shape[1] == 1:
                    # 立体声转单声道
                    return np.mean(audio_data, axis=1, keepdims=True)
        return audio_data

    def stop_processing(self):
        """停止音频处理"""
        if not self.is_running:
            print("音频处理引擎未在运行")
            return

        self.is_running = False
        if self.audio_stream:
            try:
                self.audio_stream.stop()
                self.audio_stream.close()
            except Exception as e:
                print(f"停止音频流时出错: {e}")
            self.audio_stream = None

        if self.processing_thread:
            self.processing_thread.join(timeout=2.0)
            self.processing_thread = None

        print("音频处理引擎已停止")

    def add_processor(self, node_id, processor_type, parameters):
        """添加音频处理器"""
        try:
            # 动态导入对应的处理器
            if processor_type == "gain":
                from APS.gain import GainProcessor
                processor = GainProcessor(parameters)
            elif processor_type == "delay":
                from APS.delay import DelayProcessor
                processor = DelayProcessor(parameters)
            elif processor_type == "limiter":
                from APS.limiter import LimiterProcessor
                processor = LimiterProcessor(parameters)
            elif processor_type == "input":
                from APS.input import InputProcessor
                processor = InputProcessor(parameters)
            elif processor_type == "output":
                from APS.output import OutputProcessor
                processor = OutputProcessor(parameters)
            elif processor_type == "waveform_viewer":
                from APS.waveform_viewer import WaveformViewerProcessor
                processor = WaveformViewerProcessor(parameters)

                # 为波形查看器设置参数更新回调
                def parameter_callback(param_name, value):
                    self.update_processor_parameters(node_id, {param_name: value})

                processor.set_parameter_update_callback(parameter_callback)
            elif processor_type == "fft_viewer":
                from APS.fft_viewer import FFTViewerProcessor
                processor = FFTViewerProcessor(parameters)

                # 为频谱查看器设置参数更新回调
                def parameter_callback(param_name, value):
                    self.update_processor_parameters(node_id, {param_name: value})

                processor.set_parameter_update_callback(parameter_callback)
            else:
                print(f"未知的处理器类型: {processor_type}")
                return False

            # 设置音频参数
            processor.set_sample_rate(self.sample_rate)
            processor.set_buffer_size(self.buffer_size)
            processor.set_input_channels(self.channels)
            processor.start()

            self.audio_processors[node_id] = processor
            print(f"成功添加 {processor_type} 处理器，节点ID: {node_id}")
            return True

        except Exception as e:
            print(f"添加处理器时出错: {e}")
            return False

    def remove_processor(self, node_id):
        """移除音频处理器"""
        if node_id in self.audio_processors:
            processor = self.audio_processors[node_id]
            processor.stop()
            del self.audio_processors[node_id]
            print(f"已移除处理器，节点ID: {node_id}")

    def update_processor_parameters(self, node_id, parameters):
        """更新处理器参数"""
        if node_id in self.audio_processors:
            # 将参数更新请求放入队列，在音频线程中处理
            self.parameter_queue.put(('update', node_id, parameters))
            print(f"已排队更新参数，节点ID: {node_id}, 参数: {parameters}")

    def set_processing_graph(self, graph):
        """设置处理图结构"""
        self.processing_graph = graph

    def process_audio_graph(self, audio_data, input_node_id):
        """按照处理图处理音频数据"""
        if input_node_id not in self.audio_processors:
            return None

        # 这里需要实现基于图的音频流处理
        # 简化实现：直接使用输入节点的处理器
        processor = self.audio_processors.get(input_node_id)
        if processor:
            return processor.process(audio_data)

        return audio_data

    def _audio_callback(self, indata, outdata, frames, time, status):
        """音频流回调函数"""
        # if status:
        #     print(f"音频流状态: {status}")

        # 处理参数更新
        self._process_parameter_updates()

        try:
            # 处理音频数据
            processed_data = self._process_audio_data(indata)

            # 确保输出数据形状正确
            if processed_data.shape != outdata.shape:
                print(f"形状不匹配: 输入 {indata.shape}, 输出 {processed_data.shape}, 期望 {outdata.shape}")
                # 尝试调整形状
                if processed_data.ndim == 1 and outdata.ndim == 2:
                    processed_data = processed_data.reshape(-1, 1)
                elif processed_data.ndim == 2 and outdata.ndim == 2:
                    if processed_data.shape[1] != outdata.shape[1]:
                        if processed_data.shape[1] == 1 and outdata.shape[1] == 2:
                            # 单声道转立体声
                            processed_data = np.repeat(processed_data, 2, axis=1)
                        elif processed_data.shape[1] == 2 and outdata.shape[1] == 1:
                            # 立体声转单声道
                            processed_data = np.mean(processed_data, axis=1, keepdims=True)

            # 输出处理后的音频
            outdata[:] = processed_data
        except Exception as e:
            print(f"音频处理回调错误: {e}")
            # 如果处理出错，直接输出原始数据
            outdata[:] = indata

    def _processing_loop(self):
        """音频处理主循环 - 使用 sounddevice"""
        try:
            # 获取输入输出设备索引
            input_device = self.device_manager.get_input_device(0)
            output_device = self.device_manager.get_output_device(0)

            if input_device is None or output_device is None:
                print("无法获取音频设备，使用默认设备")
                input_device = None  # 使用默认设备
                output_device = None

            print(
                f"开始音频处理循环: {self.sample_rate}Hz, {self.buffer_size} samples, 输入设备: {input_device}, 输出设备: {output_device}")

            # 创建并启动音频流
            self.audio_stream = sd.Stream(
                device=(input_device, output_device),
                samplerate=self.sample_rate,
                blocksize=self.buffer_size,
                channels=self.channels,
                dtype=np.float32,
                callback=self._audio_callback,
                latency='low'
            )

            with self.audio_stream:
                print("音频流已启动")
                while self.is_running:
                    # 主循环只需要保持运行，音频处理在回调中进行
                    time.sleep(0.1)

        except Exception as e:
            print(f"音频处理循环错误: {e}")
            self.is_running = False

    def _process_parameter_updates(self):
        """处理参数更新请求"""
        try:
            while not self.parameter_queue.empty():
                operation, node_id, data = self.parameter_queue.get_nowait()

                if operation == 'update' and node_id in self.audio_processors:
                    try:
                        self.audio_processors[node_id].update_parameters(data)
                        print(f"已更新参数，节点ID: {node_id}, 新参数: {data}")
                    except Exception as e:
                        print(f"更新参数时出错: {e}")

        except queue.Empty:
            pass
        except Exception as e:
            print(f"处理参数更新时出错: {e}")

    def _process_audio_data(self, audio_data):
        """处理音频数据"""
        # 如果没有任何处理器，直接返回原始数据
        if not self.audio_processors:
            return audio_data

        processed_data = audio_data.copy()

        # 应用所有处理器的效果
        for node_id, processor in self.audio_processors.items():
            if processor.is_active:
                try:
                    # 记录输入形状用于调试
                    input_shape = processed_data.shape
                    processed_data = processor.process(processed_data)
                    output_shape = processed_data.shape

                    # 如果形状发生变化，记录信息
                    if input_shape != output_shape:
                        print(f"处理器 {node_id} 改变了形状: {input_shape} -> {output_shape}")

                except Exception as e:
                    print(f"音频处理器错误 (节点 {node_id}): {e}")
                    # 发生错误时返回原始数据
                    return audio_data

        return processed_data

    def set_audio_settings(self, sample_rate, buffer_size, channels):
        """设置音频参数"""
        self.sample_rate = sample_rate
        self.buffer_size = buffer_size
        self.channels = channels

        # 更新所有处理器的音频设置
        for processor in self.audio_processors.values():
            processor.set_sample_rate(sample_rate)
            processor.set_buffer_size(buffer_size)
            processor.set_input_channels(channels)

        # 如果音频流正在运行，需要重新启动
        if self.is_running and self.audio_stream:
            print("音频设置已更改，重新启动音频流...")
            self.stop_processing()
            time.sleep(0.1)  # 短暂延迟确保完全停止
            self.start_processing()

    def reset_all_processors(self):
        """重置所有处理器"""
        for processor in self.audio_processors.values():
            processor.reset()
        print("所有音频处理器已重置")